// $URL: http://mingw-lib.googlecode.com/svn/trunk/working/avcodec_to_widget_8/t_av_packet.h $
// $Rev: 298 $
// $Author: akio.miyoshi $
// $Date:: 2010-07-28 14:12:27 +0000#$

#ifndef T_AV_PACKET_H
#define T_AV_PACKET_H

#include "stable.h"

class T_AVPicture_Resizable
{
public:
    AVPicture m_av_picture;
    enum PixelFormat m_pix_fmt;
    int m_width;
    int m_height;
    int m_num_bytes;
    uint8_t *m_buffer;
    explicit T_AVPicture_Resizable(PixelFormat a_pix_fmt=PIX_FMT_YUV420P, int a_width=1, int a_height=1)
        :
        m_pix_fmt(a_pix_fmt),
        m_width(a_width),
        m_height(a_height),
        m_num_bytes(0),
        m_buffer(0)
    {
        reinit();
    }
    ~T_AVPicture_Resizable()
    {
        if(m_buffer) delete[] m_buffer;
    }
#if 0x0
    void resize(int a_width, int a_height) __attribute__((deprecated))
    {
        if(m_width==a_width && m_height==a_height) return;
        m_width = a_width;
        m_height = a_height;
        reinit();
    }
#endif
    void resize(PixelFormat a_pix_fmt, int a_width, int a_height)
    {
        if(m_pix_fmt==a_pix_fmt && m_width==a_width && m_height==a_height) return;
        m_pix_fmt = a_pix_fmt;
        m_width = a_width;
        m_height = a_height;
        reinit();
    }
protected:
    void reinit()
    {
        int v_numBytes=avpicture_get_size(
                m_pix_fmt,
                m_width,
                m_height
                );
        if(m_num_bytes < v_numBytes)
        {
            m_num_bytes = v_numBytes;
            if(m_buffer) delete[] m_buffer;
            m_buffer=new uint8_t[m_num_bytes];
        }
        avpicture_fill(
                &m_av_picture,
                m_buffer,
                m_pix_fmt,
                m_width,
                m_height
                );
    }
};

class T_AV_Packet : public QObject
{
    Q_OBJECT
protected:
    AVPacket m_av_packet;
    qint64 m_timing;
public:
    int m_frame_no;
    class T_Box_Player_Context *m_player_ctx;
    AVFormatContext *m_format_ctx;
    int m_stream_index;
    AVStream *m_stream;
    AVCodecContext *m_codec_ctx;
    ~T_AV_Packet()
    {
        if(!this->isNull())
        {
            ::av_free_packet(&m_av_packet);
        }
    }
    //[isNull()]
    bool isNull()
    {
        return (m_stream==NULL);
    }
    //[decodeAudio()]
    QByteArray decodeAudio();
    //[decodeVideo()]
    bool decodeVideo(SDL_Overlay *a_overlay);
    //[decodeToQVideoFrame()]
    QVideoFrame *decodeToQVideoFrame(); //FIXME
    //[decodeToQImage()]
    QImage decodeToQImage(const QSize &a_size, int a_depth);

    //[timing()] returns packets pts in milliseconds
    qint64 timing()
    {
        return m_timing;
    }
protected:
    friend class T_AV_Packet_Queue;
    friend class T_Box_Player_Context;
    explicit T_AV_Packet(T_Box_Player_Context *a_player_ctx, AVPacket *a_av_packet);
};

class T_AV_Packet_Queue : public QObject
{
    Q_OBJECT
protected:
    class T_Box_Player_Context *m_player_ctx;
    //QMutex m_mutex;
    T_RecursiveMutex m_mutex;
    QQueue<T_AV_Packet *> packet_queue;
    int m_enqueue_count;
    bool m_complete;
public:
    explicit T_AV_Packet_Queue(class T_Box_Player_Context *a_player_ctx)
        : m_player_ctx(a_player_ctx)
        , m_enqueue_count(0)
        , m_complete(false)
    {
    }
    ~T_AV_Packet_Queue()
    {
    }
    QMutex *mutex()
    {
        return &this->m_mutex;
    }
    bool isComplete()
    {
        return m_complete;
    }
    int size()
    {
        QMutexLocker v_lock(&m_mutex);
        Q_UNUSED(v_lock);
        return packet_queue.size();
    }
    void clear()
    {
        QMutexLocker v_lock(&m_mutex);
        Q_UNUSED(v_lock);
        while(T_AV_Packet *v = this->dequeue())
        {
            delete v;
        }
        m_enqueue_count = 0;
        m_complete = false;
    }
    void enqueue(T_AV_Packet *a_av_packet)
    {
        QMutexLocker v_lock(&m_mutex);
        Q_UNUSED(v_lock);
        Q_ASSERT(!m_complete);
        if(a_av_packet==NULL)
        {
            a_av_packet = new T_AV_Packet(m_player_ctx, NULL);
            a_av_packet->m_frame_no = m_enqueue_count++;
            packet_queue.enqueue(a_av_packet);
            m_complete = true;
            return;
        }
        a_av_packet->m_frame_no = m_enqueue_count++;
        packet_queue.enqueue(a_av_packet);
    }
    T_AV_Packet *dequeue()
    {
        QMutexLocker v_lock(&m_mutex);
        Q_UNUSED(v_lock);
        if(packet_queue.size()==0) return NULL;
        return packet_queue.dequeue();
    }
    T_AV_Packet *head()
    {
        QMutexLocker v_lock(&m_mutex);
        Q_UNUSED(v_lock);
        if(packet_queue.size()==0) return NULL;
        return packet_queue.head();
    }
    T_AV_Packet *last()
    {
        QMutexLocker v_lock(&m_mutex);
        Q_UNUSED(v_lock);
        if(packet_queue.size()==0) return NULL;
        return packet_queue.last();
    }
    qint64 duration()
    {
        QMutexLocker v_lock(&m_mutex);
        Q_UNUSED(v_lock);
        if(m_complete)
        {
            return LONG_MAX;
            //return LONG_LONG_MAX;
        }
        if(packet_queue.size()==0) return 0;
        return (last()->timing() - head()->timing());
    }
};

#endif // T_AV_PACKET_H
